﻿using System;
using System.Configuration;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using Sustainalytics.Claims;
using Sustainalytics.ClientDW.DataAccessLayer.ESGRatings;
using Sustainalytics.DataLayer;
using Sustainalytics.DataLayer.ConnectionStrings;
using Sustainalytics.DataLayer.Shared;
using Sustainalytics.Entities.ConnectionStrings;
using Sustainalytics.Entities.ESGRatings;
using Sustainalytics.EsgRatings.BusinessLogic;
using Sustainalytics.ESGRatings.BusinessLogic.Models;
using Sustainalytics.ESGRatings.Service.Models;
using Sustainalytics.Utils.WebApi.ErrorCodes;

using System.IO;
using System.Collections.Specialized;
using Newtonsoft.Json;
using System.Net.Http.Headers;
using System.Net.Http.Formatting;
using Sustainalytics.Utils;
using Sustainalytics.UrlHelpers;
using Sustainalytics.DataLayer.EsgRatings;
using System.Web.Http.Cors;

namespace Sustainalytics.ESGRatings.Service.Controllers
{
    [EnableCors(origins: "*", headers: "*", methods: "*")]
    [RoutePrefix("api/v1/esgratings/weightmatrices")]
    [LogControllerFilter(true, true, false)]
    public class WeightMatricesController : ApiController
    {
        private readonly IWeightMatricesManagement _weightMatricesManagement;
        private readonly WeightMatricesProcessor _weightMatricesProcessor;
        private static IClaimsChecker _claimsChecker;
        WeightMatrixUploadDto weightMatrixUploadDto;
        readonly RepositoryCollection<ESGProfile> _esgProfileRepository;
        private readonly ResearchProductsDataReader _clientDwRepository;
        private readonly IEsgInputFileRepository _esgInputFileRepository;

        public WeightMatricesController()
        {
            var clientConnectionString = ConfigurationManager.ConnectionStrings["ClientPlatform"].ConnectionString;

            var connectionStringsManager = ConnectionStringsManager.GetInstance(ConnectionStringsRepository.GetInstance(clientConnectionString));

            var clientDwConnectionString = connectionStringsManager.GetConnectionString(ConnectionStringType.ClientDW);

            _clientDwRepository = ResearchProductsDataReader.GetInstance(clientDwConnectionString);

            _esgInputFileRepository = EsgInputFileRepository.GetInstance(new FileRepository(clientConnectionString), new RepositoryCollection<EsgInputFile>(clientConnectionString));

            var weightMatricesRepository = WeightMatricesRepository.GetInstance(clientDwConnectionString);

            var esgProfileRepository = new RepositoryCollection<ESGProfile>(clientConnectionString);

            var accountRepository = new RepositoryCollection<Entities.Account>(clientConnectionString);

            _esgProfileRepository = esgProfileRepository;

            _weightMatricesManagement = WeightMatricesManagement.GetInstance( _esgInputFileRepository, weightMatricesRepository, esgProfileRepository, accountRepository);
            _weightMatricesProcessor = new WeightMatricesProcessor(weightMatricesRepository);

        }

        public WeightMatricesController(IWeightMatricesManagement weightMatricesManagement)
        {
            _weightMatricesManagement = weightMatricesManagement;
        }

        static WeightMatricesController()
        {
            _claimsChecker = new ClaimsChecker();
        }

        //api/v1/esgratings/weightmatrices
        [Route("")]
        public HttpResponseMessage Post()
        {
            try
            {
                // if the request is not Multipart then the method is not allowed
                if (Request.Content.IsMimeMultipartContent() == false)
                {
                    return Request.CreateResponse(HttpStatusCode.MethodNotAllowed);
                }

                // weight matrix upload temporary folder
                var temporaryFolder = ConfigurationManager.AppSettings["ESGRatingWeightMatrixUploadTempFolder"];

                if (string.IsNullOrEmpty(temporaryFolder))
                {
                    throw new NullReferenceException("Weight Matrix temporary upload folder not defined in appSettings.");
                }

                // ensure that the temporary folder exists
                try
                {
                    if (Directory.Exists(temporaryFolder) == false)
                    {
                        Directory.CreateDirectory(temporaryFolder);
                    }
                }
                catch (Exception ex)
                {
                    throw new Exception(string.Format("Weight Matrix upload temporary folder does not exist and couldn't be created. Path: {0}", temporaryFolder), ex);
                }

                // create multipart provider for obtaining content associated to multipart request content
                var multipartProvider = new MultipartFormDataStreamProvider(temporaryFolder);

                // retrieve data from multipart request
                Request.Content.ReadAsMultipartAsync(multipartProvider).Wait();

                // process multipart content
                var processedContent = ProcessMultipartFormData(multipartProvider);

                FileStream stream = File.OpenRead(multipartProvider.FileData.FirstOrDefault().LocalFileName);

                byte[] matrixFileContent = new byte[stream.Length];
                stream.Read(matrixFileContent, 0, matrixFileContent.Length);
                stream.Close();

                var matrixFileContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";

                UrlFriendlyGenerator friendlyUrl = new UrlFriendlyGenerator();

                string matrixFileName = friendlyUrl.GetFriendlyName(weightMatrixUploadDto.Name) + ".xls";

                var rpIndicatorsNumbers = _clientDwRepository.ListIndicatorTemplatesNumbers().Result;
                var rootPeerGroups = _clientDwRepository.ListPeerGroupsNames().Result.ToDictionary(pg => pg, pg => pg, StringComparer.InvariantCultureIgnoreCase);
                var companyTypes = _clientDwRepository.ListCompanyTypes().Result.ToDictionary(ct => ct, ct => ct, StringComparer.InvariantCultureIgnoreCase);
                var templateTypes = _clientDwRepository.ListTemplateTypes().Result.ToDictionary(tt => tt, tt => tt, StringComparer.InvariantCultureIgnoreCase);
                var companiesIds = _clientDwRepository.ListCompaniesIds(true).Result;
                var corporateData = _clientDwRepository.ListCompaniesCorporateData(companiesIds.Values).Result;

                var fileUploadLogs = _weightMatricesProcessor.LoadWeightMatrixForProcessing(matrixFileName, weightMatrixUploadDto, matrixFileContent, matrixFileContentType,rpIndicatorsNumbers,rootPeerGroups, companyTypes,templateTypes,corporateData);

                 if (fileUploadLogs.Errors.Length == 0)
                {
                    _esgInputFileRepository.AddFile(new EsgInputFileComplete
                    {
                        Id = Guid.NewGuid(),
                        AccountId = (weightMatrixUploadDto.AccountId == null) ? null : new Guid?(new Guid(weightMatrixUploadDto.AccountId)),
                        Name = weightMatrixUploadDto.Name,
                        Type = EsgInputFileType.WeightMatrix,
                        FileName = matrixFileName,
                        Content = matrixFileContent,
                        ContentType = matrixFileContentType
                    });
                }

                MultipartFormDataContent multipartContent = CreateMultipartFormDataResponse(matrixFileContent, matrixFileName, fileUploadLogs);

                var response = this.Request.CreateResponse(HttpStatusCode.OK);

                response.Content = multipartContent;
                return response;
            }
            catch (Exception ex)
            {
                CommonEventSource.Log.Exception(ex);
                return Request.CreateResponse(HttpStatusCode.InternalServerError, new { errorCode = 100 });
            }
        }

        private HttpContent ProcessMultipartFormData(MultipartFormDataStreamProvider provider)
        {
            // read weight matrix file
            MultipartFileData weightMatrixMultipartFile = provider.FileData.FirstOrDefault();

            if (weightMatrixMultipartFile == null)
            {
                throw new Exception("No multipart weight matrix file data.");
            }

            // get weightMatrix DTO
            WeightMatrixUploadDto uploadParam = GetUploadParamsFromFormData(provider.FormData);

            // process weight Matrix file
            return ProcessWeightMatrixFile(
                uploadParam: uploadParam,
                filePath: weightMatrixMultipartFile.LocalFileName,
                fileName: weightMatrixMultipartFile.Headers.ContentDisposition.FileName,
                mimeType: weightMatrixMultipartFile.Headers.ContentType.MediaType
                );
        }

        private WeightMatrixUploadDto GetUploadParamsFromFormData(NameValueCollection formData)
        {
            string uploadParamStr = formData["uploadParam"];

            if (string.IsNullOrEmpty(uploadParamStr))
            {
                throw new Exception("WeightMatrixUploadDto is missing from request.");
            }

            var uploadParam = JsonConvert.DeserializeObject<WeightMatrixUploadDto>(uploadParamStr);

            return uploadParam;
        }

        private HttpContent ProcessWeightMatrixFile(WeightMatrixUploadDto uploadParam, string filePath, string fileName, string mimeType)
        {
            PushStreamContent processedFileContent = new PushStreamContent((stream, content, context) =>
            {
                using (var fileStream = File.OpenRead(filePath))
                {
                    fileStream.CopyTo(stream);
                }

                stream.Flush();
                stream.Close();
            }, mimeType);

            processedFileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachement")
            {
                FileName = fileName
            };
            processedFileContent.Headers.ContentType = new MediaTypeHeaderValue(mimeType);

            weightMatrixUploadDto = uploadParam;

            return processedFileContent;
        }

        private MultipartFormDataContent CreateMultipartFormDataResponse(byte[] processedFileContent, string fileName, FileUploadLogs fileUploadLogs)
        {
            MultipartFormDataContent multipartContent = new MultipartFormDataContent();

            //add errors to multipart content
            var errorsContent = new ObjectContent<string>(fileUploadLogs.Errors, new JsonMediaTypeFormatter());
            multipartContent.Add(errorsContent, "errorsParam");

            var fileContent = new ByteArrayContent(processedFileContent);
            fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
            {
                FileName = fileName
            };
            fileContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");

            //add the bytes to the multipart message
            multipartContent.Add(fileContent);

            return multipartContent;
        }

        [Route("")]
        public HttpResponseMessage Put(WeightMatrixUploadDto weightMatrix)
        {
            try
            {
            

                WeightMatrix wm = _weightMatricesManagement.GetWeightMatrix(new Guid(weightMatrix.Id));
                wm.AccountId = new Guid(weightMatrix.AccountId);
                wm.Name = weightMatrix.Name;

                wm = _weightMatricesManagement.UpdateCustomWeightMatrix(wm);

                return Request.CreateResponse(HttpStatusCode.OK, wm);
            }
            catch (Exception ex)
            {
                CommonEventSource.Log.Exception(ex);
                return Request.CreateResponse(HttpStatusCode.InternalServerError, new { errorCode = 100 });
            }
        }

        /// <summary>
        /// mark weight matrix for deletion (logical delete)
        /// </summary>
        [Route("{id}")]
        public HttpResponseMessage Delete(Guid id)
        {
            try
            {
               
                _weightMatricesManagement.DeleteCustomWeightMatrix(id);

                return this.Request.CreateResponse(HttpStatusCode.OK);
            }
            catch (Exception ex)
            {
                CommonEventSource.Log.Exception(ex);
                return Request.CreateResponse(HttpStatusCode.InternalServerError, new { errorCode = 100 });
            }
        }

        [Route("")] //weight matrices by account id
        public HttpResponseMessage Get(Guid accountId)
        {
            try
            {
                Guid? activeEsgProfileMatrixId = null;

                var activeEsgProfile = _esgProfileRepository.ReadWhere(x => x.AccountId == accountId && x.Active == ActiveTypeEnum.Active, 0, 1, null, a => a.Id, a => a.AccountId, a => a.MatrixId, a => a.MatrixName).FirstOrDefault();

                if (activeEsgProfile != null)
                {
                    activeEsgProfileMatrixId = activeEsgProfile.MatrixId;
                }


                var accountWeightMatrices = _weightMatricesManagement.ListWeightMatricesByAccountId(accountId == Guid.Empty ? (Guid?)null : accountId, false)
                                                                                .Select(x => WeightMatrixDto.CreateComplete(x, activeEsgProfileMatrixId))
                                                                                .ToList();

                return Request.CreateResponse(HttpStatusCode.OK, accountWeightMatrices);
            }
            catch (Exception ex)
            {
                CommonEventSource.Log.Exception(ex);
                return Request.CreateResponse(HttpStatusCode.InternalServerError, new { ErrorStatusCode = WebAPIGeneralErrorCodes.UnhandledException });
            }
        }

        [Route("scheduledWM/{accountId}")] //EsgInputFile scheduled weight matrices by account id
        public HttpResponseMessage Patch([FromUri] Guid accountId)
        {
            try
            {
                var accountScheduledWeightMatrices = _weightMatricesManagement.ListEsgInputFilesByAccountId(accountId);
                return Request.CreateResponse(HttpStatusCode.OK, accountScheduledWeightMatrices);
            }
            catch (Exception ex)
            {
                CommonEventSource.Log.Exception(ex);
                return Request.CreateResponse(HttpStatusCode.InternalServerError,
                                              new { ErrorStatusCode = WebAPIGeneralErrorCodes.UnhandledException });
            }
        }

        /// <summary>
        /// Gets total number of slots per platform (number of matrices of Tier 1 clients + the number of matrices in active profiles of Tier 2 clients.)
        /// </summary>
        /// <returns></returns>
        [Route("slots")]
        public HttpResponseMessage Get()
        {
            try
            {
                int count = _weightMatricesManagement.GetMatricesSlotsPerPlatform();
                return Request.CreateResponse(HttpStatusCode.OK, count);
            }
            catch (Exception ex)
            {
                CommonEventSource.Log.Exception(ex);
                return Request.CreateResponse(HttpStatusCode.InternalServerError, new { ErrorStatusCode = WebAPIGeneralErrorCodes.UnhandledException });
            }
        }

    }
}